package com.pobing.honeywell;

import android.util.Log;
import com.honeywell.aidc.*;
import com.honeywell.aidc.AidcManager.CreatedCallback;
import com.honeywell.aidc.BarcodeReader.BarcodeListener;
import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.List;

public class HoneywellScannerPlugin extends CordovaPlugin implements BarcodeListener {

    private static final String TAG = "HoneywellScanner";

    private BarcodeReader barcodeReader;
    private AidcManager manager;
    private String mConnectedScanner = null;

    private CallbackContext currentCallbackContext = null;

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);

        // We get the aidc manager at the initialization of the plugin
        AidcManager.create(this.cordova.getActivity().getApplicationContext(), new CreatedCallback() {
            @Override
            public void onCreated(AidcManager aidcManager) {
                manager = aidcManager;

                if (manager != null) {
                    Log.d(TAG, "manager aidc init success");
                    barcodeReader = manager.createBarcodeReader();
                    if (barcodeReader != null) {
                        barcodeReader.addBarcodeListener(HoneywellScannerPlugin.this);
                        try {
                            barcodeReader.claim();
                        } catch (ScannerUnavailableException e) {
                            Log.d(TAG, "barcodeReader init failed");
                            e.printStackTrace();
                        }
                    }
                } else {
                    Log.d(TAG, "manager aidc init failed");
                }
            }
        });
    }

    @Override
    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
        // This function take the name of the device to instantiate the barcodeReader
        if ("selectDevice".equals(action)) {
            Log.d(TAG, "select device");
            // We expose the current register context
            currentCallbackContext = callbackContext;
            String deviceName = (args.length() == 0 || args.getString(0).equals("null")) ? "default" : args.getString(0);

            if (manager == null) {
                callbackContext.error("No manager found please");
            } else if (deviceName != null && !deviceName.equals(mConnectedScanner)) {
                // We store the deviceName
                mConnectedScanner = deviceName;

                // We removing the old barcodeReader if exist
                if (barcodeReader != null) {
                    barcodeReader.release();
                    barcodeReader.close();
                }

                Log.d(TAG, "selected device" + deviceName);

                barcodeReader = (deviceName.equals("default")) ? manager.createBarcodeReader() : manager.createBarcodeReader(deviceName);

                if (barcodeReader != null) {
                    // We are binding the current context for barcode events
                    barcodeReader.addBarcodeListener(HoneywellScannerPlugin.this);

                    try {
                        // apply settings
                        barcodeReader.setProperty(BarcodeReader.PROPERTY_EAN_8_ENABLED, true);
                        barcodeReader.setProperty(BarcodeReader.PROPERTY_EAN_8_CHECK_DIGIT_TRANSMIT_ENABLED, true);
                        barcodeReader.setProperty(BarcodeReader.PROPERTY_EAN_13_ENABLED, true);
                        barcodeReader.setProperty(BarcodeReader.PROPERTY_EAN_13_CHECK_DIGIT_TRANSMIT_ENABLED, true);
                        barcodeReader.setProperty(BarcodeReader.PROPERTY_UPC_A_TRANSLATE_EAN13, true);

                    } catch (UnsupportedPropertyException e) {
                        callbackContext.error(e.getMessage());
                    }

                    callbackContext.success();
                } else {
                    callbackContext.error("Failed to open barcode reader with name " + deviceName);
                }
            }

            return true;
        } else if ("listConnectedBarcodeDevices".equals(action)) {
            // Return the list of connected barcode Devices
            Log.d(TAG, "listConnectedBarcodeDevices");

            if (manager == null) {
                callbackContext.error("Unable to find aidc manager");
            } else {
                this.cordova.getThreadPool().execute(new Runnable() {
                    @Override
                    public void run() {
                        List<BarcodeReaderInfo> list = manager.listConnectedBarcodeDevices();

                        try {
                            JSONObject obj = new JSONObject();
                            JSONArray arrayDevices = new JSONArray();
                            int index = 0;

                            for (BarcodeReaderInfo value : list) {
                                JSONObject objReader = new JSONObject();
                                objReader.put("name", value.getFriendlyName());
                                objReader.put("id", value.getName());

                                arrayDevices.put(objReader);
                                index++;
                            }

                            obj.put("devices", arrayDevices);
                            callbackContext.success(obj);
                        } catch (Exception x) {
                            Log.e(TAG, x.getMessage());
                            callbackContext.error(x.getMessage());
                        }
                    }
                });
            }

            return true;
        } else if ("claim".equals(action)) {
            // We claim the access to the device
            if (barcodeReader != null) {
                try {
                    barcodeReader.claim();

                    currentCallbackContext.success();
                } catch (ScannerUnavailableException e) {
                    e.printStackTrace();
                    currentCallbackContext.error("Unable to claim reader");
                }
            } else {
                currentCallbackContext.error("Reader not open");
            }
            barcodeReaderChange(false);
            return true;
        } else if ("release".equals(action)) {
            // We release the barcodeReader if exist
            if (barcodeReader != null) {
                barcodeReader.release();
                mConnectedScanner = null;
                callbackContext.success();
            } else {
                callbackContext.error("Reader not open");
            }
            barcodeReaderChange(false);
            return true;
        } else if ("register".equals(action)) {
            //We register the current callback context
            currentCallbackContext = callbackContext;

            PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
            result.setKeepCallback(true);
            callbackContext.sendPluginResult(result);

            return true;
        } else if ("unregister".equals(action)) {
            // We unregister the current callback context
            currentCallbackContext = null;

            return true;
        } else if ("enableTrigger".equals(action)) {
            // Enable the device trigger (true by default)
            Log.d(TAG, "enableTrigger");

            if (barcodeReader == null) {
                currentCallbackContext.error("no barcode reader initalized");
            } else {
                try {
                    barcodeReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_CONTROL_MODE, BarcodeReader.TRIGGER_CONTROL_MODE_AUTO_CONTROL);
                    currentCallbackContext.success();
                } catch (UnsupportedPropertyException e) {
                    currentCallbackContext.error(e.getMessage());
                }
            }

            return true;
        } else if ("disableTrigger".equals(action)) {
            // Disable the device trigger
            Log.d(TAG, "disableTrigger");

            if (barcodeReader == null) {
                currentCallbackContext.error("no barcode reader initalized");
            } else {
                try {
                    barcodeReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_CONTROL_MODE, BarcodeReader.TRIGGER_CONTROL_MODE_DISABLE);
                    currentCallbackContext.success();
                } catch (UnsupportedPropertyException e) {
                    currentCallbackContext.error(e.getMessage());
                }

                currentCallbackContext.success();
            }

            return true;
        }  else if (action.equals("softwareTriggerStart")) {
            barcodeReaderChange(true);

            return true;
        } else if (action.equals("softwareTriggerStop")) {
            barcodeReaderChange(false);
            
            return true;

        } else if (action.equals("listen")) {
            // this.currentCallbackContext = callbackContext;
            // PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
            // result.setKeepCallback(true);
            // this.currentCallbackContext.sendPluginResult(result);
            // barcodeReaderChange(false);
            this.currentCallbackContext = callbackContext;
            cordova.getThreadPool().execute(() -> {
                    PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
                    result.setKeepCallback(true);
                    callbackContext.sendPluginResult(result);
            });
            barcodeReaderChange(false);

            return true;
        }

        return false;
    }

    @Override
    public void onDestroy() {
        if (barcodeReader != null) {
            // close BarcodeReader to clean up resources.
            barcodeReader.close();
            barcodeReader = null;
        }

        if (manager != null) {
            // close AidcManager to disconnect from the scanner service.
            // once closed, the object can no longer be used.
            manager.close();
        }

        super.onDestroy();
    }

    // @Override
    // public void onBarcodeEvent(BarcodeReadEvent arg0) {
    //     Log.d(TAG, arg0.getBarcodeData());

    //     if (currentCallbackContext != null) {
    //         try {
    //             JSONObject obj = new JSONObject();
    //             obj.put("success", true);
    //             obj.put("data", arg0.getBarcodeData());

    //             PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
    //             result.setKeepCallback(true);
    //             currentCallbackContext.sendPluginResult(result);
    //         } catch (Exception x) {
    //             Log.e(TAG, x.getMessage());
    //         }
    //     }
    // }

    @Override
    public void onBarcodeEvent(BarcodeReadEvent barcodeReadEvent) {
        if (this.currentCallbackContext != null) {
            PluginResult result = new PluginResult(PluginResult.Status.OK, barcodeReadEvent.getBarcodeData());
            result.setKeepCallback(true);
            this.currentCallbackContext.sendPluginResult(result);
        }
        barcodeReaderChange(false);
    }


    @Override
    public void onFailureEvent(BarcodeFailureEvent barcodeFailureEvent) {
        Log.d(TAG, "No data");
        barcodeReaderChange(false);
    }

    private void NotifyError(String error) {
        if (this.currentCallbackContext != null) {
            PluginResult result = new PluginResult(PluginResult.Status.ERROR, error);
            result.setKeepCallback(true);
            this.currentCallbackContext.sendPluginResult(result);
        }
    }

    private void barcodeReaderChange(boolean changeStatus){
        if (barcodeReader != null) {
            try {
                barcodeReader.softwareTrigger(changeStatus);
            } catch (ScannerNotClaimedException e) {
                Log.e(TAG, e.getMessage());
                NotifyError("ScannerNotClaimedException2");
            } catch (ScannerUnavailableException e) {
                Log.e(TAG, e.getMessage());
                NotifyError("ScannerUnavailableException2");
            }
        }
    }


    // @Override
    // public void onFailureEvent(BarcodeFailureEvent arg0) {
    //     Log.d(TAG, "No data");

    //     if (currentCallbackContext != null) {
    //         try {
    //             JSONObject obj = new JSONObject();
    //             obj.put("success", false);

    //             PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
    //             result.setKeepCallback(true);
    //             currentCallbackContext.sendPluginResult(result);
    //         } catch (Exception x) {
    //             Log.e(TAG, x.getMessage());
    //         }
    //     }
    // }
}
